// $Id: impl.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#define XPCOM_SOURCE

#include <xpcom/config.hpp>
#include <exception>
#include <xpcom/assert.hpp>
#include <xpcom/memory.hpp>
#include <xpcom/string.hpp>
#include <xpcom/uuid.hpp>
#include <xpcom/impl/pstring.hpp>
#include <xpcom/impl/uuid_ptr.hpp>

namespace xpcom {
namespace impl {

// pstring_out
template <typename CharT>
pstring_out<CharT>::pstring_out(CharT** s) :
    s_(s), val_()
{
    *s_ = 0;
}

template <typename CharT>
pstring_out<CharT>::~pstring_out()
{
    if (!val_.empty()) {
        try {
            *s_ = val_.release_data();
        } catch (const std::exception&) {
            // ignore
        }
    }
}

// pstring_out_size
template <typename CharT>
pstring_out_size<CharT>::pstring_out_size(
        CharT** s, size_type* size, size_type* length) :
    s_(s), size_(size), length_(length), val_()
{
    *s_ = 0;
    *size_ = *length_ = 0;
}

template <typename CharT>
pstring_out_size<CharT>::~pstring_out_size()
{
    if (!val_.empty()) {
        try {
            *s_ = val_.release_data(length_);
            *size_ = *length_;
        } catch (const std::exception&) {
            // ignore
        }
    }
}

// pstring_out_size2
template <typename CharT>
pstring_out_size2<CharT>::pstring_out_size2(CharT** s, size_type* size) :
    s_(s), size_(size), val_()
{
    *s_ = 0;
    *size_ = 0;
}

template <typename CharT>
pstring_out_size2<CharT>::~pstring_out_size2()
{
    if (!val_.empty()) {
        try {
            *s_ = val_.release_data(size_);
        } catch (const std::exception&) {
            // ignore
        }
    }
}

// pstring_inout
template <typename CharT>
pstring_inout<CharT>::pstring_inout(CharT** s) :
    s_(s), val_(basic_string<CharT>::adopt(*s))
{
    *s_ = 0;
}

template <typename CharT>
pstring_inout<CharT>::~pstring_inout()
{
    if (!val_.empty()) {
        try {
            *s_ = val_.release_data();
        } catch (const std::exception&) {
            // ignore
        }
    }
}

// pstring_inout_size
template <typename CharT>
pstring_inout_size<CharT>::pstring_inout_size(
        CharT** s, size_type* size, size_type* length) :
    s_(s), size_(size), length_(length),
    val_(basic_string<CharT>::adopt(*s, *length)) // TODO Check it
{
    XPCOM_ASSERT(*length_ <= *size_);
    *s_ = 0;
    *size_ = *length_ = 0;
}

template <typename CharT>
pstring_inout_size<CharT>::~pstring_inout_size()
{
    if (!val_.empty()) {
        try {
            *s_ = val_.release_data(length_);
            *size_ = *length_;
        } catch (const std::exception&) {
            // ignore
        }
    }
}

// pstring_inout_size2
template <typename CharT>
pstring_inout_size2<CharT>::pstring_inout_size2(CharT** s, size_type* size) :
    s_(s), size_(size), val_(basic_string<CharT>::adopt(*s, *size))
{
    *s_ = 0;
    *size_ = 0;
}

template <typename CharT>
pstring_inout_size2<CharT>::~pstring_inout_size2()
{
    if (!val_.empty()) {
        try {
            *s_ = val_.release_data(size_);
        } catch (const std::exception&) {
            // ignore
        }
    }
}

// pstring_out_isize2
template <typename CharT>
pstring_out_isize2<CharT>::pstring_out_isize2(CharT** s, size_type size) :
    s_(s), val_(), size_(size)
{
    *s_ = 0;
}

template <typename CharT>
pstring_out_isize2<CharT>::~pstring_out_isize2()
{
    if (!val_.empty()) {
        try {
            // Check for returned string's size, if not equal to requested one
            // then there must be something wrong happened, so discard it.
            // XXX This behavior will be checked later
            XPCOM_ASSERT(val_.size() == size_);
            if (val_.size() == size_)
                *s_ = val_.release_data();
        } catch (const std::exception&) {
            // ignore
        }
    }
}

// uuid_ptr_out
uuid_ptr_out::uuid_ptr_out(uuid** id) :
    id_(id), val_(uuid::nil)
{
    *id_ = 0;
}

uuid_ptr_out::~uuid_ptr_out()
{
    if (!val_.is_nil()) {
        *id_ = ::new(mem_nothrow) uuid(val_); // TODO Handle allocation error?
    }
}

// uuid_ptr_inout
uuid_ptr_inout::uuid_ptr_inout(uuid** id) :
    id_(id), val_(*id ? **id : uuid::nil)
{
    // TODO I know this may cause one more allocation, but is more clear?
    if (*id_) {
        memory::destroy(*id_);
        *id_ = 0;
    }
}

uuid_ptr_inout::~uuid_ptr_inout()
{
    if (!val_.is_nil()) {
        *id_ = ::new(mem_nothrow) uuid(val_); // TODO Handle allocation error?
    }
}

// Explicit instantiations
template class pstring_out<char>;
template class pstring_out<wchar>;
template class pstring_out_size<char>;
template class pstring_out_size<wchar>;
template class pstring_out_size2<char>;
template class pstring_out_size2<wchar>;
template class pstring_inout<char>;
template class pstring_inout<wchar>;
template class pstring_inout_size<char>;
template class pstring_inout_size<wchar>;
template class pstring_inout_size2<char>;
template class pstring_inout_size2<wchar>;
template class pstring_out_isize2<char>;
template class pstring_out_isize2<wchar>;

} // namespace xpcom::impl
} // namespace xpcom
